#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <string.h>

#include <linux/netfilter_ipv4.h>

#include "network.h"

#define MIN_LOCAL_PORT 9001

// Alex 081010
// listen and bind
int NETWORK_listenOnPort(int port) {
  struct sockaddr_in server;
  
  int opt                = 1;
  int fd                 = socket(AF_INET, SOCK_STREAM, 0);
  server.sin_family      = AF_INET;
  server.sin_addr.s_addr = htonl(INADDR_ANY);
  server.sin_port        = htons(port);

  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
    perror("ASSERT listenOnPort- setsockopt() failed.");
    exit(1);
  }
  
  if (setsockopt(fd, IPPROTO_TCP, 1, &opt, sizeof(opt)) < 0) {
    perror("ASSERT - connect to server setsockopt() failed.");
    exit(1);
  }

  if (bind(fd, (struct sockaddr*)&server, sizeof(server)) < 0) {
	perror("ASSERT listenOnPort- bind() failed.");
    return -1;
  }

  if (listen(fd, 20) < 0) {
    perror("ASSERT listenOnPort- listen() failed.");
    exit(1);
  }
  
  return fd;
}

// Alex 081003
int NETWORK_connectToServer(int client) {
  struct sockaddr_in localAddr;
  struct sockaddr_in serverAddr;
  int fd, size;

  size = sizeof(serverAddr);
  if (getsockopt(client, SOL_IP, SO_ORIGINAL_DST, &serverAddr, &size) < 0) {//opt orginal dst
    perror("Could not determine socket's original destination.");
    close(client);
    exit(1);
  }
  
  fd = socket(AF_INET, SOCK_STREAM, 0);

  localAddr.sin_family      = AF_INET;
  localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  localAddr.sin_port        = 0;

	int opt = 1;
  if (setsockopt(fd, IPPROTO_TCP, 1, &opt, sizeof(opt)) < 0) {
    perror("ASSERT - connect to server setsockopt() failed.");
    exit(1);
  }
    //bind
  if (bind(fd, (struct sockaddr*)&localAddr, sizeof(localAddr)) < 0) {
    perror("Local bind failed.");
    close(client);
    exit(1);
  }

  if (connect(fd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
		char buf[130];
      int length;
		//char clientName[512];
		char serverName[512];
		//strcpy(clientName,inet_ntoa(localAddr.sin_addr));
		strcpy(serverName,inet_ntoa(serverAddr.sin_addr));
		snprintf(buf,length,"connection to orignial destination %s,%d failed.",
		//clientName,ntohs(localAddr.sin_port),
		serverName,ntohs(serverAddr.sin_port));
		perror(buf);
 //perror("Connect to original destination %s failed.");
    close(client);
    exit(1);
  }

  return fd;
}

// Alex 08102
int NETWORK_acceptConnection(int serverSocket) {
  struct sockaddr_in addr;
  int fd;

  int length = sizeof(addr);

  if ((fd = accept(serverSocket, (struct sockaddr*)&addr, &length)) < 0) {
    perror("ASSERT - Error on accept().");
    exit(1);
  }
  
  return fd;
}
